<template>
  <div>
    <button class="button" @click="startRolling">数字滚动</button>
    <p>
      <b>获取到的数字：</b><span>{{ randomNum }}</span>
    </p>
    <span>请输入随机数范围</span>
    <input ref="startRandom" style="width: 50px" />至<input
      ref="endRandom"
      style="width: 50px"
    />
    <div class="num-container"></div>
  </div>
</template>

<script>
export default {
  name: "RollingNum",
  data() {
    return {
      tempNum: [], //存放临时随机数数组
      randomNum: null,
      step: 5, //scroller里20个数字，每个占 100/20=5%
      numContainer: null,
      scrollerList: null,
    };
  },
  mounted() {
    this.numContainer = document.getElementsByClassName("num-container")[0];
    this.step = 5; //scroller里20个数字，每个占 100/20=5%
  },
  methods: {
    //获取随机数
    genRandomNum(start, end) {
      // Math.floor()向下取整
      // 创建一个0，1000的随机数
      return Math.floor(Math.random() * (end - start) + start); //Math.random()产生一个[0，1)之间的随机数
    },
    //获取与传入数字不同的随机数
    diffRandomNum(num) {
      const num1 = genRandomNum(start, end); //生成随机数num
      if (num === num1) {
        diffRandomNum(num);
      } else {
        return num1;
      }
    },
    // 计算y轴最快过渡距离
    getFastestTranslation(start, end) {
      let direction = 1; // -1 -> scroll up | 1 -> scroll down
      let steps = 0;
      let startTansition = this.getNormalTranslation(start);
      // console.log(start, end, 11);
      if (start > end) {
        // start - end 为 start 向 end 递减的次数
        setDirectionAndSteps(Math.abs(start - end), Math.abs(10 - start + end));
      } else if (start < end) {
        // end - start 为 start 向 end 递减的次数
        setDirectionAndSteps(Math.abs(10 - end + start), Math.abs(end - start));
      }

      startTansition += direction * steps * this.step;

      /**
       * 比较上下方向，设置最短步数
       * @param {*} dstep 向下滚动的步数
       * @param {*} ustep 向上滚动的步数
       */
      // 判断滚动最短距离是向下还是向上
      // direction:滚轮移动方向 -1👆  1👇
      // step：单个数字所占的所有数字高度的分数
      // steps：移动的步数
      function setDirectionAndSteps(dstep, ustep) {
        const isUpperFaster = dstep > ustep;
        direction = isUpperFaster ? -1 : 1;
        steps = isUpperFaster ? ustep : dstep;
      }

      return `translateY(${startTansition}%)`;
    },
    // 中间区域过渡距离
    getNormalTranslation(num) {
      // 每个 p 元素占比为100/20=5%, 第一个 0 在 25% 处出现
      return -(25 + num * 5); //此处为负数原因是要scroller向上滚动，数字向下滚动
    },
    // 复位函数
    // 如果滚轮落在了上半区或下半区，那么下一次滚动可能会不正常
    // 因此每次超出中间区就执行一次没有过渡的 translateY 将滚轮恢复到中间区
    reposition({ style }, num) {
      const { transitionDuration } = style;
      const normalTranslation = `translateY(${this.getNormalTranslation(
        num
      )}%)`;
      // 判断移动的距离是否在中间区域内
      if (style.transform !== normalTranslation) {
        style.transitionDuration = "0s";
        // 直接变换到与移动距离相同的中间区域
        style.transform = normalTranslation;
        // requestAnimationFrame(
        //   requestAnimationFrame.bind(null, () => {
        setTimeout(() => {
          //在transiion里面，当连续两个duration的属性设置紧挨着时，会默认以最后一个duration属性为值,可以采用定时器的方式解决。
          style.transitionDuration = transitionDuration;
        }, 0);
        //   })
        // );
      }
    },
    startRolling() {
      const startNum = this.$refs.startRandom.value * 1;
      const endRandomNum = this.$refs.endRandom.value * 1;
      if (!startNum || !endRandomNum) {
        alert("请输入要摇号的随机数范围");
        return;
      }
      for (let i = 0; i < this.$refs.endRandom.value.length; i++) {
        if (this.tempNum.length < i + 1) {
          this.tempNum.push(0);
        }
      }
      this.randomNum = this.genRandomNum(startNum, endRandomNum) + "";
      let finishNum = this.randomNum.split("");
      finishNum = finishNum.map((item) => item * 1);
      // 将numContainer的子节点清空，重新放置数字框框
      this.numContainer.innerHTML = "";
      for (let i = 0; i < finishNum.length; i++) {
        const div = document.createElement("div");
        div.className = "scroller-window";
        div.innerHTML = `
            <div class="single-scroller">
              <p>5</p>
              <p>6</p>
              <p>7</p>
              <p>8</p>
              <p>9</p>
              <p>0</p>
              <p>1</p>
              <p>2</p>
              <p>3</p>
              <p>4</p>
              <p>5</p>
              <p>6</p>
              <p>7</p>
              <p>8</p>
              <p>9</p>
              <p>0</p>
              <p>1</p>
              <p>2</p>
              <p>3</p>
              <p>4</p>
            </div>
          `;
        this.numContainer.appendChild(div);
      }
      this.scrollerList = document.getElementsByClassName("single-scroller");
      this.scrollerList = Array.from(this.scrollerList);
      //将所有展示的元素置零（首次运行时清零）或者复位：此时默认展示的是第一个元素5，将元素转移到中间0的位置
      this.scrollerList.forEach((scroller, index) => {
        scroller.start = this.tempNum[index];
        scroller.style.transform = `translateY(${this.getNormalTranslation(
          scroller.start
        )}%)`;
      });
      // 使用定时器原因，开始展示的数据和摇号后的数据要有一定的时间才能进行滚动
      setTimeout(() => {
        this.scrollerList.forEach((scroller, index) => {
          scroller.end = finishNum[index];
          // console.log(scroller.start, scroller.end, "start,end");
          scroller.style.transform = this.getFastestTranslation(
            scroller.start,
            scroller.end
          );
          scroller.start = scroller.end;
          //transitionend 该事件在 CSS 完成过渡后触发。
          scroller.addEventListener("transitionend", ({ target }) => {
            this.reposition(target, scroller.end);
          });
          this.tempNum = finishNum;
        });
      });
    },
  },
};
</script>

<style lang="less">
.num-container {
  display: flex;
  .scroller-window {
    height: 3em;
    width: 15%;
    margin: 10% 5%;
    overflow: hidden;
    border: 1px solid black;
    border-radius: 5px;
    box-shadow: 0 0 10px black inset;
  }
  .single-scroller {
    transition: transform ease-in 1s;
    p {
      width: 100%;
      height: 3em;
      line-height: 1.5em;
      margin: 0;
      text-align: center;
      font-size: 2em;
    }
  }
}
</style>
